﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace Vtron.LEDCorrective.EDSDKWrapper
{
    public class EDSDKHandler
    {
        private static readonly EDSDKHandler instance = new EDSDKHandler();
        public static EDSDKHandler Instance
        {
            get
            {
                return instance;
            }
        }

        private string path = String.Empty;
        private EDSDKHandler()
        {
            string directoryPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Vtron\\LEDCorrective";
            path = String.Format("{0}\\{1}", directoryPath, "RemotePhoto");
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
        }

        private event EDSDK.EdsPropertyEventHandler propertyEventHandle;
        private EDSDK.EdsObjectEventHandler ObjectEventHandle;
        private event EDSDK.EdsStateEventHandler stateEventHandle;

        public void TakePhoto(uint tv, uint iso)
        {
            IntPtr cameraList;
            int cameraCount;
            IntPtr cam;
            //Vtron.LEDCorrective.EDSDKWrapper.EDSDK.EdsDeviceInfo deviceInfo;

            EDSDK.EdsInitializeSDK();
            EDSDK.EdsGetCameraList(out cameraList);
            EDSDK.EdsGetChildCount(cameraList, out cameraCount);
            EDSDK.EdsGetChildAtIndex(cameraList, 0, out cam);
            //EDSDK.EdsGetDeviceInfo(cam, out deviceInfo);
            EDSDK.EdsSetPropertyEventHandler(cam, EDSDK.PropertyEvent_All, propertyEventHandle, IntPtr.Zero);
            ObjectEventHandle = new EDSDK.EdsObjectEventHandler(ObjectEventCallBack);
            EDSDK.EdsSetObjectEventHandler(cam, EDSDK.ObjectEvent_All, ObjectEventHandle, IntPtr.Zero);
            EDSDK.EdsSetCameraStateEventHandler(cam, EDSDK.StateEvent_All, stateEventHandle, IntPtr.Zero);
            EDSDK.EdsOpenSession(cam);

            EDSDK.EdsSetPropertyData(cam, EDSDK.PropID_SaveTo, 0, 4, (uint)EDSDK.EdsSaveTo.Host);
            EDSDK.EdsSetPropertyData(cam, EDSDK.PropID_ImageQuality, 0, 4, (uint)EDSDK.ImageQuality.EdsImageQuality_LR);

            //EDSDK.EdsSetPropertyData(cam, EDSDK.PropID_Av, 0, 4, (uint)0x58);
            EDSDK.EdsSetPropertyData(cam, EDSDK.PropID_Tv, 0, 4, tv);
            EDSDK.EdsSetPropertyData(cam, EDSDK.PropID_ISOSpeed, 0, 4, iso);

            EDSDK.EdsCapacity capacity = default(EDSDK.EdsCapacity);
            capacity.NumberOfFreeClusters = 0x10000000;
            capacity.BytesPerSector = 0x0200;
            capacity.Reset = 1;
            EDSDK.EdsSetCapacity(cam, capacity);

            EDSDK.EdsSendCommand(cam, EDSDK.CameraCommand_TakePicture, 0);

            //EDSDK.EdsCloseSession(cam);
            //EDSDK.EdsRelease(cam);
            //EDSDK.EdsTerminateSDK();
        }

        public void StartLiveview(Action<UnmanagedMemoryStream> action)
        {
            IntPtr cameraList;
            int cameraCount;
            IntPtr cam;

            EDSDK.EdsInitializeSDK();
            EDSDK.EdsGetCameraList(out cameraList);
            EDSDK.EdsGetChildCount(cameraList, out cameraCount);
            EDSDK.EdsGetChildAtIndex(cameraList, 0, out cam);
            EDSDK.EdsOpenSession(cam);

            uint PropID = (uint)EDSDK.PropID_Evf_OutputDevice;
            unsafe
            {
                uint property = 0;
                EDSDK.EdsDataType dataType;
                int dataSize;
                uint data;

                //先获取原来的EVF输出设备属性的信息
                IntPtr ptr = new IntPtr(&property);
                EDSDK.EdsGetPropertySize(cam, PropID, 0, out dataType, out dataSize);
                EDSDK.EdsGetPropertyData(cam, PropID, 0, out data);

                //设置输出到PC 
                IntPtr device = new IntPtr();
                device = new IntPtr(data | EDSDK.EvfOutputDevice_PC);
                EDSDK.EdsSetPropertyData(cam, EDSDK.PropID_Evf_OutputDevice, 0, Marshal.SizeOf(device), device);

                //开始实时取景 
                while (true)
                {
                    IntPtr _stream = IntPtr.Zero;
                    IntPtr _image = new IntPtr();
                    IntPtr _imageData = new IntPtr();
                    uint _imageLen;

                    EDSDK.EdsCreateMemoryStream(0, out _stream);
                    EDSDK.EdsCreateEvfImageRef(_stream, out _image);

                    EDSDK.EdsDownloadEvfImage(cam, _image);

                    //显示图象  
                    EDSDK.EdsGetPointer(_stream, out _imageData);
                    EDSDK.EdsGetLength(_stream, out _imageLen);

                    UnmanagedMemoryStream ums = new UnmanagedMemoryStream((byte*)_imageData.ToPointer(), _imageLen, _imageLen, FileAccess.Read);
                    action(ums);

                    if (_stream != IntPtr.Zero)
                    {
                        EDSDK.EdsRelease(_stream);
                        _stream = IntPtr.Zero;
                    }
                    if (_image != IntPtr.Zero)
                    {
                        EDSDK.EdsRelease(_image);
                        _image = IntPtr.Zero;
                    }
                }
            }
        }

        public List<int> GetAVList()
        {
            IntPtr cameraList;
            int cameraCount;
            IntPtr cam;

            EDSDK.EdsInitializeSDK();
            EDSDK.EdsGetCameraList(out cameraList);
            EDSDK.EdsGetChildCount(cameraList, out cameraCount);
            EDSDK.EdsGetChildAtIndex(cameraList, 0, out cam);
            EDSDK.EdsOpenSession(cam);

            EDSDK.EdsPropertyDesc des;
            EDSDK.EdsGetPropertyDesc(cam, (uint)EDSDK.PropID_Av, out des);
            return des.PropDesc.Take(des.NumElements).ToList();
        }

        public List<int> GetTVList()
        {
            IntPtr cameraList;
            int cameraCount;
            IntPtr cam;

            EDSDK.EdsInitializeSDK();
            EDSDK.EdsGetCameraList(out cameraList);
            EDSDK.EdsGetChildCount(cameraList, out cameraCount);
            EDSDK.EdsGetChildAtIndex(cameraList, 0, out cam);
            EDSDK.EdsOpenSession(cam);

            EDSDK.EdsPropertyDesc des;
            EDSDK.EdsGetPropertyDesc(cam, (uint)EDSDK.PropID_Tv, out des);
            return des.PropDesc.Take(des.NumElements).ToList();
        }

        public uint GetAVSetting()
        {
            IntPtr cameraList;
            int cameraCount;
            IntPtr cam;

            EDSDK.EdsInitializeSDK();
            EDSDK.EdsGetCameraList(out cameraList);
            EDSDK.EdsGetChildCount(cameraList, out cameraCount);
            EDSDK.EdsGetChildAtIndex(cameraList, 0, out cam);

            uint PropID = (uint)EDSDK.PropID_Av;
            unsafe
            {
                uint property = 0;
                EDSDK.EdsDataType dataType;
                int dataSize;
                IntPtr ptr = new IntPtr(&property);
                EDSDK.EdsGetPropertySize(cam, PropID, 0, out dataType, out dataSize);
                EDSDK.EdsGetPropertyData(cam, PropID, 0, dataSize, ptr);
                return property;
            }
        }

        public uint GetTVSetting()
        {
            IntPtr cameraList;
            int cameraCount;
            IntPtr cam;

            EDSDK.EdsInitializeSDK();
            EDSDK.EdsGetCameraList(out cameraList);
            EDSDK.EdsGetChildCount(cameraList, out cameraCount);
            EDSDK.EdsGetChildAtIndex(cameraList, 0, out cam);

            uint PropID = (uint)EDSDK.PropID_Tv;
            unsafe
            {
                uint property = 0;
                EDSDK.EdsDataType dataType;
                int dataSize;
                IntPtr ptr = new IntPtr(&property);
                EDSDK.EdsGetPropertySize(cam, PropID, 0, out dataType, out dataSize);
                EDSDK.EdsGetPropertyData(cam, PropID, 0, dataSize, ptr);
                return property;
            }
        }

        private void DownloadImage(String Path, IntPtr DirItem)
        {
            uint Err = 0;

            EDSDK.EdsDirectoryItemInfo DirInfo;

            Err = EDSDK.EdsGetDirectoryItemInfo(DirItem, out DirInfo);
            if (Err != 0) throw new Exception();

            IntPtr Stream;
            Path += "\\" + DirInfo.szFileName;
            Err = EDSDK.EdsCreateFileStream(Path, EDSDK.EdsFileCreateDisposition.CreateAlways, EDSDK.EdsAccess.ReadWrite, out Stream);
            if (Err != 0) throw new Exception();

            Err = EDSDK.EdsDownload(DirItem, DirInfo.Size, Stream);
            if (Err != 0) throw new Exception();

            Err = EDSDK.EdsDownloadComplete(DirItem);
            if (Err != 0) throw new Exception();

            Err = EDSDK.EdsRelease(Stream);
            if (Err != 0) throw new Exception();

            while (!System.IO.File.Exists(Path))
                Thread.Sleep(100);
        }

        private uint ObjectEventCallBack(uint Event, IntPtr Object, IntPtr Context)
        {
            switch (Event)
            {
                case EDSDK.ObjectEvent_All:
                    break;
                case EDSDK.ObjectEvent_DirItemCancelTransferDT:
                    break;
                case EDSDK.ObjectEvent_DirItemContentChanged:
                    break;
                case EDSDK.ObjectEvent_DirItemCreated:
                    break;
                case EDSDK.ObjectEvent_DirItemInfoChanged:
                    break;
                case EDSDK.ObjectEvent_DirItemRemoved:
                    break;
                case EDSDK.ObjectEvent_DirItemRequestTransfer:
                    DownloadImage(path, Object);
                    break;
                case EDSDK.ObjectEvent_DirItemRequestTransferDT:
                    break;
                case EDSDK.ObjectEvent_FolderUpdateItems:
                    break;
                case EDSDK.ObjectEvent_VolumeAdded:
                    break;
                case EDSDK.ObjectEvent_VolumeInfoChanged:
                    break;
                case EDSDK.ObjectEvent_VolumeRemoved:
                    break;
                case EDSDK.ObjectEvent_VolumeUpdateItems:
                    break;
            }

            return EDSDK.EDS_ERR_OK;
        }

    }
}
